package com.example.mqreceiver.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.*;
import org.springframework.amqp.support.converter.ClassMapper;
import org.springframework.amqp.support.converter.DefaultJackson2JavaTypeMapper;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * 字典服务-rabbitmq配置
 *
 * @author ke
 * Created by on 2021-05-16 15:52
 */

@Configuration
public class RabbitMqConfig {

    @Value("${rabbitdemo.event.exchange.name}")
    private String exchangeName;

    @Value("${rabbitdemo.event.routing.key}")
    private String routingKey;

    @Value("${rabbitdemo.event.queue.name}")
    private String queueName;

    @Value("${rabbitdemo.event.retry.times}")
    private int retryTimes;

    public static final String RABBIT_HEADER_RETRY_KEY = "x-retry";

    @Bean(name = "iDicEventQueue")
    public Queue iDicEventQueue() {
        return new Queue(queueName);
    }

    @Bean(name = "iDicTopicExchange")
    public TopicExchange iDicTopicExchange() {
        return new TopicExchange(exchangeName);
    }

    @Bean(name = "iDicEventBinding")
    public Binding iDicEventBinding(@Qualifier("iDicEventQueue") Queue queue,
                                    @Qualifier("iDicTopicExchange") TopicExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(routingKey);
    }

    @Bean(name = "retryIdicEventQueue")
    public Queue retryiDicEventQueue() {
        Map<String, Object> map = new HashMap<>(3);
        map.put("x-dead-letter-exchange", exchangeName);
        map.put("x-dead-letter-routing-key", routingKey);
        map.put("x-message-ttl", 5 * 1000);
        return new Queue(getRetryExchangeName(), true, false, false, map);
    }

    @Bean(name = "retryIdicTopicExchange")
    public TopicExchange retryTopicExchange() {
        return new TopicExchange(getRetryExchangeName());
    }

    @Bean(name = "retryIdicEventBinding")
    public Binding retryDicEventBinding(@Qualifier("retryIdicEventQueue") Queue queue,
                                        @Qualifier("retryIdicTopicExchange") TopicExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(routingKey);
    }

    @Bean(name = "failedIdicEventQueue")
    public Queue failedIdicEventQueue() {
        return new Queue(getFailedQueueName());
    }

    @Bean(name = "failedIdicTopicExchange")
    public TopicExchange failedIdicTopicExchange() {
        return new TopicExchange(getFailedExchangeName());
    }

    @Bean(name = "failedIdicEventBinding")
    public Binding failedIdicEventBinding(@Qualifier("failedIdicEventQueue") Queue queue,
                                          @Qualifier("failedIdicTopicExchange") TopicExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(routingKey);
    }

    /**
     * 如果消息发送entity与消息接收entity在同一个工程
     * 直接指定Jackson2JsonMessageConverter进行反序列化
     *
     * @return
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 如果消息发送entity与消息接收entity不在同一个工程
     * 可以指定通用反序列化对象接收后，在对数据进行处理
     *
     * @return
     */
    /*
    @Bean
    public MessageConverter messageConverter() {
        ObjectMapper objectMapper = new ObjectMapper();
        //设置转换的实体类可见性[避免属性访问权问题所导致的缺少字段]
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter(objectMapper);
        //因为实体类的包名不同的问题；重写 classMapper映射，不然接收消息异常报错
        jackson2JsonMessageConverter.setClassMapper(new ClassMapper() {

            @Override
            public void fromClass(Class<?> aClass, MessageProperties messageProperties) {
                //  messageProperties.setHeader("__TypeId__", "com.gjpzh.robotcrm.authentication.entity.Resource");
            }
            @Override
            public Class<?> toClass(MessageProperties messageProperties) {

                String typeId = messageProperties.getHeaders().get("__TypeId__") +"";
                try {
                    if (StringUtils.isBlank(typeId) || "null".equals(typeId)) {
                        return new DefaultJackson2JavaTypeMapper().toClass(messageProperties);
                    } else {
                        return Class.forName("event.BusinessDataInfoEvent");
                    }

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
        return jackson2JsonMessageConverter;
    }*/
    public String getRetryExchangeName() {
        return exchangeName + ".retry";
    }

    public String getRetryQueueName() {
        return queueName + "@retry";
    }

    public String getFailedExchangeName() {
        return exchangeName + "@failed";
    }

    public String getFailedQueueName() {
        return queueName + "@failed";
    }

    public String getExchangeName() {
        return exchangeName;
    }

    public void setExchangeName(String exchangeName) {
        this.exchangeName = exchangeName;
    }

    public String getRoutingKey() {
        return routingKey;
    }

    public void setRoutingKey(String routingKey) {
        this.routingKey = routingKey;
    }

    public String getQueueName() {
        return queueName;
    }

    public void setQueueName(String queueName) {
        this.queueName = queueName;
    }

    public int getRetryTimes() {
        return retryTimes;
    }

    public void setRetryTimes(int retryTimes) {
        this.retryTimes = retryTimes;
    }
}
